package com.reger.l2cache.aspect;

import java.lang.reflect.Method;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.expression.MethodBasedEvaluationContext;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;

import com.reger.l2cache.annotation.L2Cache;
import com.reger.l2cache.annotation.L2DelCache;
import com.reger.l2cache.annotation.L2PutCache;
import com.reger.l2cache.core.Cache;
import com.reger.l2cache.core.LocalCache;

@Aspect
public class L2CacheAspect implements DisposableBean,InitializingBean{

	private static final Logger log = LoggerFactory.getLogger(L2CacheAspect.class);

	private final static String L2ANNOTATION = "com.reger.l2cache.annotation.L2Cache";
	private final static String L2DELANNOTATION = "com.reger.l2cache.annotation.L2DelCache";
	private final static String L2PUTANNOTATION = "com.reger.l2cache.annotation.L2PutCache";

	private final static ExpressionParser parser = new SpelExpressionParser();

	private final static ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();

	private final Cache cache;
	
	public L2CacheAspect(Cache cache) {
		super();
		this.cache = cache;
	}

	private final static String buildSpElKey(String expressionString, Object rootObject, Method method, Object[] arguments) {
		if (expressionString == null || (expressionString=expressionString.trim()).isEmpty()){
			log.warn("{}.{}以生成缓存key，的模板为空", method.getDeclaringClass(), method.getName() );
			return null;
		}
		try { 
			MethodBasedEvaluationContext context = new MethodBasedEvaluationContext(rootObject, method, arguments, parameterNameDiscoverer);
			Expression expression = parser.parseExpression(expressionString);
			Object cacheKey=expression.getValue(context);
			return String.valueOf(cacheKey );
		} catch (Exception e) {
			log.warn("{}.{}以{}生成缓存key异常，生成的缓存key是null", method.getDeclaringClass(), method.getName(), expressionString, e);
			return null;
		}
	}
	

	@Around("@annotation(" + L2ANNOTATION + ")")
	public Object l2Annotation(ProceedingJoinPoint point) throws Throwable {
		Method method = ((MethodSignature) point.getSignature()).getMethod();
		L2Cache l2Cache = method.getAnnotation(L2Cache.class);
		if (cache == null) {
			return point.proceed();
		}

		Object rootObject = point.getTarget();
		Object[] args = point.getArgs();
		String cacheKey = buildSpElKey(l2Cache.key(), rootObject, method, args);
		if (cacheKey == null) {
			return point.proceed();
		}
		return cache.invoking(cacheKey, l2Cache.timeOut(), method, rootObject, args);
	}

	@Around("@annotation(" + L2DELANNOTATION + ")")
	public Object l2DelAnnotation(ProceedingJoinPoint point) throws Throwable {
		try {
			return point.proceed();
		} finally {
			Method method = ((MethodSignature) point.getSignature()).getMethod();
			try {
				Object[] args = point.getArgs();
				Object rootObject = point.getTarget();
				L2DelCache l2Cache = method.getAnnotation(L2DelCache.class);
				String cacheKey = buildSpElKey(l2Cache.key(), rootObject, method, args);
				cache.removeData(cacheKey);
			} catch (Exception e) {
				log.warn("清除缓存失败 {}.{}",method.getDeclaringClass(),method.getName(),e);
			}
		}
	}
	
	@Around("@annotation(" + L2PUTANNOTATION + ")")
	public Object l2PutAnnotation(ProceedingJoinPoint point) throws Throwable {
		Object relust = point.proceed();
		Method method = ((MethodSignature) point.getSignature()).getMethod();
		try {
			Object[] args = point.getArgs();
			Object rootObject = point.getTarget();
			L2PutCache l2Cache = method.getAnnotation(L2PutCache.class);
			String cacheKey = buildSpElKey(l2Cache.key(), rootObject, method, args);
			cache.putData(cacheKey, relust, l2Cache.timeOut());
		} catch (Exception e) {
			log.warn("清除缓存失败 {}.{}", method.getDeclaringClass(), method.getName(), e);
		}
		return relust;
	}


	@Override
	public void afterPropertiesSet() throws Exception {
	
	}
	
	@Override
	public void destroy() throws Exception {
		log.info("应用关闭...");
		 if (cache instanceof LocalCache) {
			LocalCache localCache = (LocalCache) cache;
			localCache.destroy();
		}
	}
	
}
